#include "net_tcp.h"
#include <sys/time.h>
namespace net {
#define ASSERT_RET(expr, ret, fmt, ...)                                                                                \
    if (!(expr)) {                                                                                                     \
        printf(fmt, ##__VA_ARGS__);                                                                                    \
        return ret;                                                                                                    \
    }

// cat /proc/sys/fs/epoll/max_user_watches

//思路：
// 1、本服务设计是考虑在设备端使用的，连接在10个以内。
// 2、每个连接都可以考虑使用一个线程去管理。
// 3、使用超时阻塞模式。
//缺点：
// 1、线程如果太多内核调度增加资源消耗。

void IOListener::BindIO(int fd, int events, void* userdata)
{
    epoll_event ev;
    ev.data.ptr = userdata;
    ev.events   = EPOLLET | EPOLLRDHUP;

    if (events & IO_READ)
        ev.events |= EPOLLIN;
    if (events & IO_WRITE)
        ev.events |= EPOLLOUT;

    epoll_ctl(epoll_, EPOLL_CTL_ADD, fd, &ev);
}

void IOListener::UnbindIO(int fd)
{
    epoll_ctl(epoll_, EPOLL_CTL_DEL, fd, NULL);
}

//**************************************************************
// Socket
//**************************************************************

Socket::Socket()
{
    ctx = -1;
}

Socket::~Socket()
{
    Close();
}

bool Socket::Connect(const std::string& ip, int port)
{
    ASSERT_RET(ctx < 0, false, "Socket::Connect failed. Already connected!");

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));

    addr.sin_family = AF_INET;
    addr.sin_port   = htons(port);

    ASSERT_RET(inet_pton(AF_INET, ip.c_str(), &addr.sin_addr) == 1, false,
               "Socket::Connect failed. Bad remote address : %s!", ip.c_str());

    int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    ASSERT_RET(fd >= 0, false, "Socket::Connect failed due to create socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)");

    int flags = fcntl(fd, F_GETFL, 0);
    if (fcntl(fd, F_SETFL, O_NONBLOCK | flags) < 0) {
        close(fd);
        printf("Socket::Connect failed due to fcntl(fd, F_SETFL, O_NONBLOCK | flags)");
        return false;
    }

    if (connect(fd, (sockaddr*)&addr, sizeof(sockaddr)) < 0) {
        if (errno != EINPROGRESS) {
            close(fd);
            printf("Socket::Connect failed with errcode : %d", errno);
            return false;
        }

        int wait_fd = epoll_create(256);
        ASSERT_RET(wait_fd >= 0, false, "Socket::Connect failed due to epoll_create(1) for async waiting");

        epoll_event ev;
        ev.events = EPOLLET | EPOLLOUT | EPOLLRDHUP;
        epoll_ctl(wait_fd, EPOLL_CTL_ADD, fd, &ev);

        int count = epoll_wait(wait_fd, &ev, 1, 1000);
        close(wait_fd);
        if (count < 0 || ev.events & EPOLLRDHUP) {
            close(fd);
            printf("Socket::Connect failed due to timeout");
            return false;
        }
    }

    ctx = (int)fd;
    return true;
}

bool Socket::Listen(const std::string& ip, int port)
{
    ASSERT_RET(ctx < 0, false, "Socket::Listen failed. Already connected!");

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));

    addr.sin_family = AF_INET;
    addr.sin_port   = htons(port);

    ASSERT_RET(inet_pton(AF_INET, ip.c_str(), &addr.sin_addr) == 1, false,
               "Socket::Listen failed. Bad listen address : %s!", ip.c_str());

    int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    ASSERT_RET(fd >= 0, false, "Socket::Listen failed due to create socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)");

    // int flags = fcntl(fd, F_GETFL, 0);
    // if (fcntl(fd, F_SETFL, O_NONBLOCK | flags) < 0) {
    //     close(fd);
    //     printf("Socket::Listen failed due to fcntl(fd, F_SETFL, O_NONBLOCK | flags)");
    //     return false;
    // }

    int reuse = 1;
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

    if (::bind(fd, (sockaddr*)&addr, sizeof(sockaddr)) < 0 || ::listen(fd, 1024) < 0) {
        close(fd);
        printf("Socket::Listen failed due to bind() or listen()");
        return false;
    }

    ctx = (int)fd;
    return true;
}

Socket* Socket::Accept()
{
    if (ctx < 0)
        return nullptr;

    sockaddr_in addr      = {0};
    socklen_t   addr_size = sizeof(addr);
    int         fd        = accept(ctx, (sockaddr*)&addr, &addr_size);
    if (fd < 0)
        return nullptr;

    int flags = fcntl(fd, F_GETFL, 0);
    if (fcntl(fd, F_SETFL, O_NONBLOCK | flags) < 0) {
        close(fd);
        return nullptr;
    }

    Socket* socket = new Socket();
    socket->ctx    = fd;
    socket->ip     = addr.sin_addr.s_addr;
    //查看发送缓存
    // cat /proc/sys/net/ipv4/tcp_wmem
    // int nSendBuf = 1024 * 1024;  //设置为1M
    // setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const char*)&nSendBuf, sizeof(int));

    return socket;
}

int Socket::Recv(char* buf, size_t size)
{
    if (ctx < 0)
        return -1;
    int recved = (int)recv(ctx, buf, (int)size, MSG_DONTWAIT);
    if (recved > 0)
        return recved;
    if (recved < 0 && errno == EAGAIN)
        return 0;
    return recved == 0 ? -1 : -2;
}

bool Socket::GetHeartBeatStatus()
{
    struct timeval cur_t, t_curs_t_;
    curs_t_mtx_.lock();
    t_curs_t_ = curs_t_;
    curs_t_mtx_.unlock();
    gettimeofday(&cur_t, NULL);
    // printf("cur_t: %d t_curs_t_:%d  dh:%d\n", cur_t.tv_sec, t_curs_t_.tv_sec, (cur_t.tv_sec - t_curs_t_.tv_sec));
    if ((cur_t.tv_sec - t_curs_t_.tv_sec) > 5) {
        return true;
    }
    else {
        return false;
    }
}

bool Socket::Send(const char* data, size_t size)
{
    curs_t_mtx_.lock();
    gettimeofday(&curs_t_, NULL);
    curs_t_mtx_.unlock();
    std::unique_lock<std::mutex> lck(snd_mutex_);
    if (ctx < 0)
        return false;

    char* ptr  = (char*)data;
    int   sent = 0;
    int   left = (int)size;

    while (true) {
        sent = (int)send(ctx, ptr, left, MSG_DONTWAIT);
        if (sent < 0) {
            if (errno == EAGAIN) {
                usleep(1000);
            }
            else {
                return false;
            }
        }
        else if (sent < left) {
            left -= sent;
            ptr += sent;
        }
        else if (sent == left) {
            return true;
        }
        else {
            return left == 0;
        }
    }
}

void Socket::Close()
{
    shutdown(ctx, SHUT_RDWR);
    if (ctx < 0)
        return;
    close(ctx);
    ctx = -1;
}

}  // namespace net